package thread_01;
//定时器需求
//1.定义一个类，表示一个任务   Runnable任务本体，time任务的执行时间（换成绝对时间）
//2.引入数据结构，来保存多个任务（用优先级队列，减少遍历开销）
//3.引入扫描线程，不停地循环，获取队首元素，判断是否到时间。  到时间就执行并且出队，没到则继续下次循环

//4.引入锁，针对队列的操作上锁，解决线程安全问题
//5.解决忙等问题，引入wait和notify   ---  队列为空，需要wait  ---  队首元素没到时间也需要wait

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

//把任务对象，通过优先级队列管理起来
//怎么样的对象能放进优先级队列？
//因此要实现Comparable<MyTimeTask>接口
class MyTimeTask implements Comparable<MyTimeTask>{
    private Runnable runnable;
//    啥时候要去执行，此处的time不是delay，而是毫秒级别的时间戳
    private long time;
    public MyTimeTask(Runnable runnable,long delay){
        this.runnable = runnable;
//        手动换算
        this.time = System.currentTimeMillis() + delay;
    }

    public void run(){
        runnable.run();
    }

    public long getTime(){
        return time;
    }

    @Override
    public int compareTo(MyTimeTask o) {
//        按照世家来进行比较，期望最终构造出来小堆
//        return (int) (o.time - this.time);
        return (int) (this.time - o.time);
    }
}

class MyTimer{
//    加锁先要有锁对象,也可以直接使用this
    private Object locker = new Object();
//    开销大
//    private List<MyTimeTask> taskList = new ArrayList<>();
    private PriorityQueue<MyTimeTask> taskQueue = new PriorityQueue<>();
//    3.需要线程来执行runnable任务。构造方法中，创建线程
    public MyTimer(){
//        这个线程负责不停扫描上述队列队首元素，确定是否要执行任务
        Thread t = new Thread(() ->{
            try {
                while (true){
                    synchronized (locker){
                        if (taskQueue.size() == 0){
//                      r如果队列为空，将不会执行MyTimeTask task = taskQueue.poll();
//                            continue;
                            locker.wait();
                        }
//                    从队列中获取元素
//                        MyTimeTask task = taskQueue.poll();
                        MyTimeTask task = taskQueue.peek();
//                    再来判断时间
                        long curTime = System.currentTimeMillis();
                        if (curTime >= task.getTime()){
//                        如果是，要执行任务
                            task.run();
                            taskQueue.poll();
                        }else {
//                        时间没到
//                            continue;  "空转”  浪费CPU资源
//                            不可以用sleep，如果在休眠过程中来个任务，无法像wait那样被notify唤醒
//                            wait休眠期间释放锁，sleep休眠期间抱着锁
                            locker.wait(task.getTime() - curTime);
                        }
                    }
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        });
        t.start();
    }

    public void schedule(Runnable runnable,long delay){
        synchronized (locker){
//            入队列
            MyTimeTask task = new MyTimeTask(runnable,delay);
            taskQueue.offer(task);
            locker.notify();
        }
    }

}
public class Demo38 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 为什么这些代码是这样的 3000");
            }
        },3000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 为什么这些代码是这样的 2000");
            }
        },2000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 为什么这些代码是这样的 1000");
            }
        },1000);
    }

}
